﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using MvcContrib.TestHelper;
using MvcContrib.TestHelper.Fakes;
using ProgressTracker.Domain;
using ProgressTracker.Domain.Contracts;
using ProgressTracker.Domain.Messages;
using ProgressTracker.WebUI.Controllers;
using ProgressTracker.WebUI.Models.Progress;
using ProgressTracker.WebUI.Models.Shared;
using ProgressTracker.WebUI.Translation;
using System;
using System.Collections.Generic;

namespace ProgressTracker.Tests.Controllers
{
    [TestClass]
    public class ProgressControllerTests
    {
        private Mock<IDayModelMapper> _dayMapper;
        private Mock<IDayService> _dayService;

        private DateTime _testDateTime;

        private Day _testDay;
        private TaskForDay _taskToUpdate;

        private DayModel _testDayModel;
        private TaskModel _testTaskModel;

        private UpdateDayModel _updateModel;
        private UpdateTaskModel _updateTaskModel;

        [TestInitialize]
        public void Initialize()
        {
            _testDateTime = new DateTime(2001, 01, 19);

            _dayMapper = new Mock<IDayModelMapper>();

            _dayService = new Mock<IDayService>();

            _testDay = new Day
            {
                Date = new DateTime(2001, 1, 19),
                Notes = "Notes",
                TasksForDay = new List<TaskForDay>()
            };


            _taskToUpdate = new TaskForDay
            {
                Id = 1,
                IsComplete = false,
                Name = "Task"
            };

            _testTaskModel = new TaskModel
            {
                Description = "Test Description",
                IsComplete = true,
                Name = "Test"
            };

            _testDayModel = new DayModel
            {
                Day = _testDateTime,
                Notes = "Notes",
                TasksForDay = new List<TaskModel> { _testTaskModel }
            };

            _updateTaskModel = new UpdateTaskModel { Id = _taskToUpdate.Id, Complete = true };

            _updateModel = new UpdateDayModel
            {
                Date = _testDateTime,
                TasksToUpdate = new List<UpdateTaskModel>
                {
                    _updateTaskModel
                }
            };
        }

        [TestMethod]
        public void Test_Day_Renders_View_If_No_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>())).Returns(_testDay);

            var progressController = CreateController();
            var view = progressController.Day(null);

            view.AssertViewRendered();
        }

        [TestMethod]
        public void Test_Day_Gets_Current_Day_If_No_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>())).Returns(_testDay).Verifiable();

            var progressController = CreateController();
            var view = progressController.Day(null);

            _dayService.Verify();
        }

        [TestMethod]
        public void Test_Day_Gets_Renders_View_If_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(
                It.IsAny<DateTime>(), 
                It.IsAny<string>())).Returns(_testDay);

            var progressController = CreateController();
            var view = progressController.Day(_testDateTime);

            view.AssertViewRendered();
        }

        [TestMethod]
        public void Test_Day_Gets_Correct_Date_If_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(It.Is<DateTime>(
                d => d.Date.Date == _testDateTime), 
                It.IsAny<string>())).Returns(_testDay).Verifiable();

            var progressController = CreateController();
            var view = progressController.Day(_testDateTime);

            _dayService.Verify();
        }

        [TestMethod]
        public void Test_DayTable_Renders_View_If_No_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>())).Returns(_testDay);

            var progressController = CreateController();
            var view = progressController.DayTable(null);

            view.AssertPartialViewRendered();
        }

        [TestMethod]
        public void Test_DayTable_Gets_Current_Day_If_No_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>())).Returns(_testDay).Verifiable();

            var progressController = CreateController();
            var view = progressController.DayTable(null);

            _dayService.Verify();
        }

        [TestMethod]
        public void Test_DayTable_Gets_Renders_View_If_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(
                It.IsAny<DateTime>(),
                It.IsAny<string>())).Returns(_testDay);

            var progressController = CreateController();
            var view = progressController.DayTable(_testDateTime);

            view.AssertPartialViewRendered();
        }

        [TestMethod]
        public void Test_DayTable_Gets_Correct_Date_If_Date_Provided()
        {
            _dayService.Setup(x => x.GetDay(It.Is<DateTime>(
                d => d.Date.Date == _testDateTime),
                It.IsAny<string>())).Returns(_testDay).Verifiable();

            var progressController = CreateController();
            var view = progressController.DayTable(_testDateTime);

            _dayService.Verify();
        }

        [TestMethod]
        public void Test_Update_Retrieves_Correct_Day_To_Update()
        {
            _dayService.Setup(x => x.GetDay(
                It.Is<DateTime>(d => d.Equals(_testDateTime)),
                It.IsAny<string>()))
                .Returns(_testDay);

            var progressController = CreateController();
            var view = progressController.Update(_updateModel);

            _dayService.Verify();
        }

        [TestMethod]
        public void Test_Update_Updates_Task_Status_For_Day()
        {
            var dayWithTasks = new Day
            {
                Date = _testDay.Date,
                Notes = _testDay.Notes,
                TasksForDay = new List<TaskForDay>
                {
                    _taskToUpdate
                }
            };

            _dayService.Setup(x => x.GetDay(
                It.IsAny<DateTime>(),
                It.IsAny<string>()))
                .Returns(dayWithTasks);

            _dayService.Setup(x => x.UpdateTaskCompleteness(
                It.Is<UpdateTaskCompletenessRequest>(t => t.TaskId == _taskToUpdate.Id)
                )).Verifiable();

            var progressController = CreateController();
            var view = progressController.Update(_updateModel);

            _dayService.Verify();
        }

        [TestMethod]
        public void Test_Update_Updates_Day_Info()
        {
            _dayService.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>()))
                .Returns(_testDay);

            _dayService.Setup(x => x.UpdateDay(It.Is<UpdateDayRequest>(
                u => u.Notes == _updateModel.Notes))).Verifiable();

            var progressController = CreateController();
            var view = progressController.Update(_updateModel);

            _dayService.Verify();
        }

        private ProgressController CreateController()
        {
            //need to generate fake http context and fake principal to utilize 'User' variable.
            var user = new FakePrincipal(new FakeIdentity("fake@fake.com"), null);

            var builder = new TestControllerBuilder();
            builder.HttpContext.User = user;

            var progressController = new ProgressController(_dayService.Object,_dayMapper.Object);
            builder.InitializeController(progressController);
            
            return progressController;
        }
    }
}
